home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 4 / QRZ Ham Radio Callsign Database - Volume 4.iso / files / tcpip / amiga / asrc29p.lha / ftpcli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-29  |  24.9 KB  |  1,095 lines

  1. /* FTP client (interactive user) code */
  2. #include <stdio.h>
  3. #include "global.h"
  4. #include "mbuf.h"
  5. #include "session.h"
  6. #include "cmdparse.h"
  7. #include "timer.h"
  8. #include "proc.h"
  9. #include "tty.h"
  10. #include "socket.h"
  11. #include "ftp.h"
  12. #include "ftpcli.h"
  13. #include "commands.h"
  14. #include "netuser.h"
  15. #include "config.h"
  16.  
  17. #define    DIRBUF    256
  18.  
  19. static int doascii __ARGS((int argc,char *argv[],void *p));
  20. static int dobinary __ARGS((int argc,char *argv[],void *p));
  21. static int dobyte __ARGS((int argc,char *argv[],void *p));
  22. static int doftpcd __ARGS((int argc,char *argv[],void *p));
  23. static int doftphelp __ARGS((int argc,char *argv[],void *p));
  24. static int doget __ARGS((int argc,char *argv[],void *p));
  25. static int dohash __ARGS((int argc,char *argv[],void *p));
  26. static int dolist __ARGS((int argc,char *argv[],void *p));
  27. static int dols __ARGS((int argc,char *argv[],void *p));
  28. static int domkdir __ARGS((int argc,char *argv[],void *p));
  29. static int domget __ARGS((int argc,char *argv[],void *p));
  30. static int donothing __ARGS((int argc,char *argv[],void *p));
  31. static int dopaging __ARGS((int argc,char *argv[],void *p));
  32. static int doput __ARGS((int argc,char *argv[],void *p));
  33. static int doquit __ARGS((int argc,char *argv[],void *p));
  34. static int dormdir __ARGS((int argc,char *argv[],void *p));
  35. static int dormfile __ARGS((int argc,char *argv[],void *p));
  36. static int dotype __ARGS((int argc,char *argv[],void *p));
  37. static int doview __ARGS((int argc,char *argv[],void *p));
  38. static int doverbose __ARGS((int argc,char *argv[],void *p));
  39. static int doprompt __ARGS((int argc,char *argv[],void *p));
  40.  
  41. extern int docd __ARGS((int argc,char *argv[],void *p));
  42. extern int dodir __ARGS((int argc,char *argv[],void *p));
  43.  
  44. static int getline __ARGS((struct session *sp,char *prompt,char *buf,int n));
  45. static int getresp __ARGS((struct ftpcli *ftp,int mincode));
  46. static int getsub __ARGS((struct ftpcli *ftp,char *command,char *remotename,
  47.     char *localname));
  48. static void sendport __ARGS((int s,struct sockaddr_in *socket));
  49.  
  50. static char *ftpcli_login __ARGS((struct ftpcli *ftp,char *host));
  51.  
  52. static char Notsess[] = "Not an FTP session!\n";
  53.  
  54. static struct cmds Ftpcmds[] = {
  55.     "",        donothing,    0, 0, NULLCHAR,
  56.     "ascii",    doascii,    0, 0, NULLCHAR,
  57.     "binary",    dobinary,    0, 0, NULLCHAR,
  58.     "byte",        dobyte,        0, 0, NULLCHAR,
  59.     "cd",        doftpcd,    0, 2, "cd <directory>",
  60.     "delete",    dormfile,    0, 2, "delete <filename>",
  61.     "dir",        dolist,        0, 0, NULLCHAR,
  62.     "erase",    dormfile,    0, 2, "erase <filename>",
  63.     "get",        doget,        0, 2, "get remotefile <localfile>",
  64.     "hash",        dohash,        0, 0, NULLCHAR,
  65.     "help",        doftphelp,    0, 0, NULLCHAR,
  66.     "interactive",    doprompt,    0, 0, NULLCHAR,
  67.     "lcd",        docd,        0, 0, NULLCHAR,
  68.     "ldir",        dodir,        0, 0, NULLCHAR,
  69.     "list",        dolist,        0, 0, NULLCHAR,
  70.     "ls",        dols,        0, 0, NULLCHAR,
  71.     "mget",        domget,        0, 2, "mget <file> [<file> ...]",
  72.     "mkdir",    domkdir,    0, 2, "mkdir <directory>",
  73.     "nlst",        dols,        0, 0, NULLCHAR,
  74.     "paging",    dopaging,    0, 0, NULLCHAR,
  75.     "prompt",    doprompt,    0, 0, NULLCHAR,
  76.     "put",        doput,        0, 2, "put localfile <remotefile>",
  77.     "quit",        doquit,        0, 0, NULLCHAR,
  78.     "rmdir",    dormdir,    0, 2, "rmdir <directory>",
  79.     "type",        dotype,        0, 0, NULLCHAR,
  80.     "verbose",    doverbose,    0, 0, NULLCHAR,
  81.     "view",         doview,         0, 2,"view <remotefile>",
  82.     "?",        doftphelp,    0, 0, NULLCHAR,
  83.     NULLCHAR,    NULLFP,        0, 0, NULLCHAR,
  84. };
  85.  
  86. /* Handle top-level FTP command */
  87. int doftp(argc,argv,p)
  88. int argc;
  89. char *argv[];
  90. void *p;
  91. {
  92.     struct session *sp;
  93.     struct ftpcli ftp;
  94.     struct sockaddr_in fsocket;
  95.     int resp,vsave;
  96.     char *buf,*bufsav,*cp;
  97.     int control;
  98.  
  99.     /* Allocate a session control block */
  100.     if((sp = newsession(argv[1],FTP,0)) == NULLSESSION){
  101.         tprintf("Too many sessions\n");
  102.         freeargs(argc,argv);
  103.         return 1;
  104.     }
  105.     Current->flowmode = 0;
  106.     memset((char *)&ftp,0,sizeof(ftp));
  107.     ftp.control = ftp.data = -1;
  108.     ftp.verbose = V_NORMAL;
  109.     ftp.type = IMAGE_TYPE;
  110.     ftp.prompt = TRUE;
  111.  
  112.     sp->cb.ftp = &ftp;    /* Downward link */
  113.     ftp.session = sp;    /* Upward link */
  114.  
  115.     fsocket.sin_family = AF_INET;
  116.     if(argc < 3)
  117.         fsocket.sin_port = IPPORT_FTP;
  118.     else 
  119.         fsocket.sin_port = atoi(argv[2]);
  120.  
  121.     freeargs(argc,argv);
  122.     tprintf("Resolving %s... ",sp->name);
  123.     if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
  124.         tprintf(Badhost,sp->name);
  125.         keywait(NULLCHAR,1);
  126.         freesession(sp);
  127.         return 1;
  128.     }
  129.     /* Open the control connection */
  130.     if((control = sp->s = ftp.control = socket(AF_INET,SOCK_STREAM,0)) == -1){
  131.         tprintf("Can't create socket\n");
  132.         keywait(NULLCHAR,1);
  133.         freesession(sp);
  134.         return 1;
  135.     }
  136.     tprintf("Trying %s...\n",psocket((struct sockaddr *)&fsocket));
  137.     if(connect(control,(char *)&fsocket,sizeof(fsocket)) == -1)
  138.         goto quit;
  139.     tprintf("FTP session %u connected to %s\n",(unsigned)(sp-Sessions),
  140.         sp->name);
  141.  
  142.     /* Wait for greeting from server */
  143.     resp = getresp(&ftp,200);
  144.  
  145.     if(resp >= 400)
  146.         goto quit;
  147.     /* Now process responses and commands */
  148.     buf = mallocw(LINELEN);
  149.     while(resp != -1){
  150.         if(resp == 220) {
  151.             /* Sign-on banner; prompt for and send USER command */
  152.             if((cp = ftpcli_login(&ftp, sp->name)) == NULLCHAR){
  153.                 getline(sp,"Enter user name: ",buf,LINELEN);
  154.                 /* Send the command only if the user response
  155.                  * was non-null
  156.                  */
  157.                 if(buf[0] != '\n') {
  158.                     usprintf(control,"USER %s",buf);
  159.                     resp = getresp(&ftp,200);
  160.                 } else
  161.                     resp = 200;    /* dummy */
  162.             } else {
  163.                 usprintf(control,"USER %s\n",cp);
  164.                 free(cp);
  165.                 resp = getresp(&ftp,200);
  166.             }
  167.         } else if(resp == 331) {
  168.             if(ftp.password == NULLCHAR){
  169.                 /* turn off echo */
  170.                 sp->ttystate.echo = 0;
  171.                 getline(sp,"Password: ",buf,LINELEN);
  172.                 tprintf("\n");
  173.                 /* Turn echo back on */
  174.                 sp->ttystate.echo = 1;
  175.                 /* Send the command only if the user response
  176.                  * was non-null
  177.                  */
  178.                 if(buf[0] != '\n') {
  179.                     usprintf(control,"PASS %s",buf);
  180.                     resp = getresp(&ftp,200);
  181.                 } else
  182.                     resp = 200;    /* dummy */
  183.             } else {
  184.                 usprintf(control,"PASS %s\n",ftp.password);
  185.                 resp = getresp(&ftp,200);
  186.                 free(ftp.password);
  187.                 ftp.password = NULLCHAR;    /* clean up */
  188.             }
  189.         } else {
  190.             /* Test the control channel first */
  191.             if(sockstate(control) == NULLCHAR)
  192.                 break;
  193.  
  194.             getline(sp,"ftp> ",buf,LINELEN);
  195.  
  196.             /* Copy because cmdparse modifies the original */
  197.             bufsav = strdup(buf);
  198.             if((resp = cmdparse(Ftpcmds,buf,&ftp)) != -1){
  199.                 /* Valid command, free buffer and get another */
  200.                 free(bufsav);
  201.             } else {
  202.                 /* Not a local cmd, send to remote server */
  203.                 usputs(control,bufsav);
  204.                 free(bufsav);
  205.  
  206.                 /* Enable display of server response */
  207.                 vsave = ftp.verbose;
  208.                 ftp.verbose = V_NORMAL;
  209.                 resp = getresp(&ftp,200);
  210.                 ftp.verbose = vsave;
  211.             }
  212.         }
  213.     }
  214.     free(buf);
  215. quit:    cp = sockerr(control);
  216.     tprintf("FTP session %u closed: %s\n",(unsigned)(sp - Sessions),
  217.      cp != NULLCHAR ? cp : "EOF");
  218.  
  219.     if(ftp.fp != NULLFILE && ftp.fp != stdout)
  220.         fclose(ftp.fp);
  221.     if(ftp.data != -1)
  222.         close_s(ftp.data);
  223.     if(ftp.control != -1)
  224.         close_s(ftp.control);
  225.     keywait(NULLCHAR,1);
  226.     if(ftp.session != NULLSESSION)
  227.         freesession(ftp.session);
  228.     return 0;
  229. }
  230.  
  231. /* Control flow control */
  232. static int dopaging(argc,argv,p)
  233. int argc;
  234. char *argv[];
  235. void *p;
  236. {
  237.     return setbool(&Current->flowmode,"Screen Paging",argc,argv);
  238. }
  239.  
  240. /* Interactive MGET control */
  241. static int doprompt(argc,argv,p)
  242. int argc;
  243. char *argv[];
  244. void *p;
  245. {
  246.     register struct ftpcli *ftp;
  247.  
  248.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  249.         return -1;
  250.     return setbool(&ftp->prompt,"Interactive Prompting",argc,argv);
  251. }
  252.  
  253. /* Control verbosity level */
  254. static int doverbose(argc,argv,p)
  255. int argc;
  256. char *argv[];
  257. void *p;
  258. {
  259.     register struct ftpcli *ftp;
  260.  
  261.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  262.         return -1;
  263.     return setshort(&ftp->verbose,"Verbose",argc,argv);
  264. }
  265.  
  266. /* Set verbosity to high (convenience command) */
  267. static int dohash(argc,argv,p)
  268. int argc;
  269. char *argv[];
  270. void *p;
  271. {
  272.     register struct ftpcli *ftp;
  273.  
  274.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  275.         return -1;
  276.     ftp->verbose = V_HASH;
  277.     tprintf("Hash Marks every 1024 bytes\n");
  278.     return 0;
  279. }
  280.     
  281. /* Set verbosity to very high (convenience command) */
  282. static int dobyte(argc,argv,p)
  283. int argc;
  284. char *argv[];
  285. void *p;
  286. {
  287.     register struct ftpcli *ftp;
  288.  
  289.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  290.         return -1;
  291.     ftp->verbose = V_BYTE;
  292.     tprintf("Rolling Byte count during GET/PUT\n");
  293.     return 0;
  294. }
  295.     
  296. /* Handle null line to avoid trapping on first command in table */
  297. static int donothing(argc,argv,p)
  298. int argc;
  299. char *argv[];
  300. void *p;
  301. {
  302.     return 0;
  303. }
  304.  
  305. /* Close session */
  306. static int doquit(argc,argv,p)
  307. int argc;
  308. char *argv[];
  309. void *p;
  310. {
  311.     register struct ftpcli *ftp;
  312.  
  313.     ftp = (struct ftpcli *)p;
  314.     if(ftp == NULLFTP)
  315.         return -1;
  316.     usprintf(ftp->control,"QUIT\n");
  317.     getresp(ftp,200);    /* Get the closing message */
  318.     getresp(ftp,200);    /* Wait for the server to close */
  319.     return -1;
  320. }
  321.  
  322. /* Translate 'cd' to 'cwd' for convenience */
  323. static int doftpcd(argc,argv,p)
  324. int argc;
  325. char *argv[];
  326. void *p;
  327. {
  328.     register struct ftpcli *ftp;
  329.  
  330.     ftp = (struct ftpcli *)p;
  331.     if(ftp == NULLFTP)
  332.         return -1;
  333.     usprintf(ftp->control,"CWD %s\n",argv[1]);
  334.     return getresp(ftp,200);
  335. }
  336.  
  337. /* Get a collection of files */
  338. static int domget(argc,argv,p)
  339. int argc;
  340. char *argv[];
  341. void *p;
  342. {
  343.     register struct ftpcli *ftp;
  344.     FILE *files, *filel;
  345.     char tmpname[80], locget[80];
  346.     char *buf, *local;
  347.     int i, c;
  348.     long r;
  349.  
  350.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  351.         tprintf(Notsess);
  352.         return 1;
  353.     }
  354.  
  355.     tmpnam(tmpname);
  356.     buf = mallocw(DIRBUF);
  357.  
  358.     ftp->prompt = TRUE;
  359.     ftp->state = RECEIVING_STATE;
  360.  
  361.     for(i=1;i<argc;i++){
  362.         if(argv[i][0] == '@') {
  363.             if((filel = fopen(&argv[i][1], "r")) == NULLFILE){
  364.                 tprintf("Can't open listfile: %s\n", &argv[i][1]);
  365.                 continue;
  366.             }
  367.             if((files = fopen(tmpname, "w")) == NULLFILE){
  368.                 tprintf("Can't open tempfile: %s\n", tmpname);
  369.                 fclose(filel);
  370.                 continue;
  371.             }
  372.             while(fgets(buf,DIRBUF,filel) != NULLCHAR){
  373.                 fputs(buf,files);
  374.             }
  375.             fclose(files);
  376.             fclose(filel);
  377.             if((files = fopen(tmpname, "r")) == NULLFILE){
  378.                 tprintf("Can't open tempfile: %s\n", tmpname);
  379.                 fclose(filel);
  380.                 continue;
  381.             }
  382.         } else {
  383.             r = getsub(ftp,"NLST",argv[i],tmpname);
  384.             if(ftp->abort)
  385.                 break;    /* Aborted */
  386.             if(r == -1 || (files = fopen(tmpname,"r")) == NULLFILE){
  387.                 tprintf("Can't NLST %s\n",argv[i]);
  388.                 unlink(tmpname);
  389.                 continue;
  390.             }
  391.         }
  392.         /* The tmp file now contains a list of the remote files, so
  393.          * go get 'em. Break out if the user signals an abort.
  394.          */
  395.         while(fgets(buf,DIRBUF,files) != NULLCHAR){
  396.             rip(buf);
  397.             if(buf[strlen(buf)-1] == '\r')
  398.                 buf[strlen(buf)-1] = '\0';
  399.             local = strdup(buf);
  400.  
  401.             if(ftp->prompt) {
  402.                 sprintf(locget,"GET '%s' (y,n,a,q) ? ",local);
  403.                 c = keywait(locget,1);
  404.                 if(c == 'q') {
  405.                     ftp->abort = TRUE;
  406.                     goto mgquit;
  407.                 }
  408.                 if(c == 'a') 
  409.                     ftp->prompt = FALSE;
  410.                 if(c == 'y' || c == 'a') 
  411.                     getsub(ftp,"RETR",buf,local);
  412.                 else 
  413.                     tprintf(" - File '%s' Skipped\n", local);
  414.             } else
  415.                 getsub(ftp,"RETR",buf,local); 
  416.  
  417. mgquit:            free(local);
  418.             if(ftp->abort){
  419.                 /* User abort */
  420.                 ftp->abort = 0;
  421.                 fclose(files);
  422.                 unlink(tmpname);
  423.                 free(buf);
  424.                 ftp->state = COMMAND_STATE;
  425.                 return 1;
  426.             }
  427.         }
  428.         fclose(files);
  429.         unlink(tmpname);
  430.     }
  431.     free(buf);
  432.     ftp->state = COMMAND_STATE;
  433.     ftp->abort = 0;
  434.     return 0;
  435. }
  436.  
  437. /* Translate 'mkdir' to 'xmkd' for convenience */
  438. static int domkdir(argc,argv,p)
  439. int argc;
  440. char *argv[];
  441. void *p;
  442. {
  443.     register struct ftpcli *ftp;
  444.  
  445.     ftp = (struct ftpcli *)p;
  446.     if(ftp == NULLFTP)
  447.         return -1;
  448.     usprintf(ftp->control,"XMKD %s\n",argv[1]);
  449.     return getresp(ftp,200);
  450. }
  451.  
  452. /* Translate 'rmdir' to 'xrmd' for convenience */
  453. static int dormdir(argc,argv,p)
  454. int argc;
  455. char *argv[];
  456. void *p;
  457. {
  458.     register struct ftpcli *ftp;
  459.  
  460.     ftp = (struct ftpcli *)p;
  461.     if(ftp == NULLFTP)
  462.         return -1;
  463.     usprintf(ftp->control,"XRMD %s\n",argv[1]);
  464.     return getresp(ftp,200);
  465. }
  466.  
  467. /* Translate 'delete' or 'erase' to 'dele' for convenience */
  468. static int dormfile(argc,argv,p)
  469. int argc;
  470. char *argv[];
  471. void *p;
  472. {
  473.     register struct ftpcli *ftp;
  474.  
  475.     ftp = (struct ftpcli *)p;
  476.     if(ftp == NULLFTP)
  477.         return -1;
  478.     usprintf(ftp->control,"DELE %s\n",argv[1]);
  479.     return getresp(ftp,200);
  480. }
  481.  
  482. static int dobinary(argc,argv,p)
  483. int argc;
  484. char *argv[];
  485. void *p;
  486. {
  487.     char *args[2];
  488.  
  489.     args[1] = "I";
  490.     return dotype(2,args,p);
  491. }
  492.  
  493. static int doascii(argc,argv,p)
  494. int argc;
  495. char *argv[];
  496. void *p;
  497. {
  498.     char *args[2];
  499.  
  500.     args[1] = "A";
  501.     return dotype(2,args,p);
  502. }
  503.  
  504. /* Handle "type" command from user */
  505. static int dotype(argc,argv,p)
  506. int argc;
  507. char *argv[];
  508. void *p;
  509. {
  510.     register struct ftpcli *ftp;
  511.  
  512.     ftp = (struct ftpcli *)p;
  513.     if(ftp == NULLFTP)
  514.         return -1;
  515.     if(argc < 2){
  516.         switch(ftp->type){
  517.         case IMAGE_TYPE:
  518.             tprintf("Image\n");
  519.             break;
  520.         case ASCII_TYPE:
  521.             tprintf("Ascii\n");
  522.             break;
  523.         case LOGICAL_TYPE:
  524.             tprintf("Logical bytesize %u\n",ftp->logbsize);
  525.             break;
  526.         }
  527.         return 0;
  528.     }
  529.     switch(*argv[1]){
  530.     case 'i':
  531.     case 'I':
  532.     case 'b':
  533.     case 'B':
  534.         ftp->typesent = ftp->type = IMAGE_TYPE;
  535.         usprintf(ftp->control,"TYPE I\n");
  536.         break;
  537.     case 'a':
  538.     case 'A':
  539.         ftp->typesent = ftp->type = ASCII_TYPE;
  540.         usprintf(ftp->control,"TYPE A\n");
  541.         break;
  542.     case 'L':
  543.     case 'l':
  544.         ftp->typesent = ftp->type = LOGICAL_TYPE;
  545.         ftp->logbsize = atoi(argv[2]);
  546.         usprintf(ftp->control,"TYPE L %s\n",argv[2]);
  547.         break;
  548.     default:
  549.         tprintf("Invalid type %s\n",argv[1]);
  550.         return 1;
  551.     }
  552.     return getresp(ftp,200);
  553. }
  554.  
  555. /* Start view transfer. Syntax: view <remote name> */
  556. static int doview(argc,argv,p)
  557. int argc;
  558. char *argv[];
  559. void *p;
  560. {
  561.     char *remotename;
  562.     register struct ftpcli  *ftp;
  563.  
  564.     ftp = (struct ftpcli *)p;
  565.     if(ftp == NULLFTP){
  566.     tputs(Notsess);
  567.         return 1;
  568.     }
  569.     remotename = argv[1];
  570.  
  571.     return getsub(ftp,"RETR",remotename,NULLCHAR);
  572. }
  573.  
  574. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  575. static int doget(argc,argv,p)
  576. int argc;
  577. char *argv[];
  578. void *p;
  579. {
  580.     char *remotename,*localname;
  581.     register struct ftpcli *ftp;
  582.  
  583.     ftp = (struct ftpcli *)p;
  584.     if(ftp == NULLFTP){
  585.         tprintf(Notsess);
  586.         return 1;
  587.     }
  588.     remotename = argv[1];
  589.     if(argc < 3)
  590.         localname = remotename;
  591.     else
  592.         localname = argv[2];
  593.  
  594.     return getsub(ftp,"RETR",remotename,localname);
  595. }
  596.  
  597. /* List remote directory. Syntax: dir <remote files> [<local name>] */
  598. static int dolist(argc,argv,p)
  599. int argc;
  600. char *argv[];
  601. void *p;
  602. {
  603.     char *remotename,*localname;
  604.     register struct ftpcli *ftp;
  605.  
  606.     ftp = (struct ftpcli *)p;
  607.     if(ftp == NULLFTP){
  608.         tprintf(Notsess);
  609.         return 1;
  610.     }
  611.     remotename = argv[1];
  612.     if(argc > 2)
  613.         localname = argv[2];
  614.     else
  615.         localname = NULLCHAR;
  616.  
  617.     return getsub(ftp,"LIST",remotename,localname);
  618. }
  619.  
  620. /* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
  621. static int dols(argc,argv,p)
  622. int argc;
  623. char *argv[];
  624. void *p;
  625. {
  626.     char *remotename,*localname;
  627.     register struct ftpcli *ftp;
  628.  
  629.     ftp = (struct ftpcli *)p;
  630.     if(ftp == NULLFTP){
  631.         tprintf(Notsess);
  632.         return 1;
  633.     }
  634.     remotename = argv[1];
  635.     if(argc > 2)
  636.         localname = argv[2];
  637.     else
  638.         localname = NULLCHAR;
  639.  
  640.     return getsub(ftp,"NLST",remotename,localname);
  641. }
  642.  
  643. /* Common code to LIST/NLST/RETR/VIEW */
  644. static int getsub(ftp,command,remotename,localname)
  645. register struct ftpcli *ftp;
  646. char *command,*remotename,*localname;
  647. {
  648.     unsigned long total;
  649.     FILE *fp;
  650.     int cnt,resp,i,control;
  651.     char *mode;
  652.     struct sockaddr_in lsocket;
  653.     struct sockaddr_in lcsocket;
  654.     int32 startclk,rate;
  655.     int vsave;
  656.  
  657.     if(ftp == NULLFTP)
  658.         return -1;
  659.     control = ftp->control;
  660.  
  661.     switch(ftp->type){
  662.     case IMAGE_TYPE:
  663.     case LOGICAL_TYPE:
  664.         mode = WRITE_BINARY;
  665.         break;
  666.     case ASCII_TYPE:
  667.         mode = WRITE_TEXT;
  668.         break;
  669.     }
  670.     /* Send TYPE message, if necessary */
  671.     if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0){
  672.         if(ftp->typesent != ASCII_TYPE){
  673.             /* Directory listings are always in ASCII */
  674.             usprintf(control,"TYPE A\n");
  675.             ftp->typesent = ASCII_TYPE;
  676.             resp = getresp(ftp,200);
  677.             if(resp == -1 || resp > 299){
  678.                 return 1;
  679.             }
  680.         }
  681.     } else if(ftp->typesent != ftp->type){
  682.         switch(ftp->type){
  683.         case ASCII_TYPE:
  684.             usprintf(control,"TYPE A\n");
  685.             break;
  686.         case IMAGE_TYPE:
  687.             usprintf(control,"TYPE I\n");
  688.             break;
  689.         case LOGICAL_TYPE:
  690.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  691.             break;
  692.         }
  693.         ftp->typesent = ftp->type;
  694.         resp = getresp(ftp,200);
  695.         if(resp == -1 || resp > 299){
  696.             return 1;
  697.         }
  698.     }
  699.     if(localname == NULLCHAR){
  700.         fp = NULLFILE;
  701.     } else if((fp = fopen(localname,mode)) == NULLFILE){
  702.         tprintf("Can't write %s: %s\n",localname,sys_errlist[errno]);
  703.         return 1;
  704.     }
  705.     /* Open the data connection */
  706.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  707.     listen(ftp->data,0);    /* Accept only one connection */
  708.     ftp->state = RECEIVING_STATE;
  709.  
  710.     /* Send the PORT message and wait for ack. Use the IP address
  711.      * on the local end of our control connection.
  712.      */
  713.     i = SOCKSIZE;
  714.     getsockname(ftp->data,(char *)&lsocket,&i);
  715.     i = SOCKSIZE;
  716.     getsockname(ftp->control,(char *)&lcsocket,&i);
  717.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  718.     sendport(control,&lsocket);
  719.     resp = getresp(ftp,200);
  720.     if(resp == -1 || resp > 299){
  721.         /* Error, quit */
  722.         if(fp != NULLFILE && fp != stdout)
  723.             fclose(fp);
  724.         close_s(ftp->data);
  725.         ftp->data = -1;
  726.         ftp->state = COMMAND_STATE;
  727.         return 1;
  728.     }
  729.     /* Generate the command to start the transfer and wait for ack */
  730.     if(remotename != NULLCHAR)
  731.         usprintf(control,"%s %s\n",command,remotename);
  732.     else
  733.         usprintf(control,"%s\n",command);
  734.     /* Get the intermediate "150" response */
  735.     resp = getresp(ftp,100);
  736.     if(resp == -1 || resp >= 400){
  737.         /* Error, quit */
  738.         if(fp != NULLFILE && fp != stdout)
  739.             fclose(fp);
  740.         close_s(ftp->data);
  741.         ftp->data = -1;
  742.         ftp->state = COMMAND_STATE;
  743.         return 1;
  744.     }
  745.     /* Wait for the server to open the data connection */
  746.     cnt = 0;
  747.     ftp->data = accept(ftp->data,NULLCHAR,&cnt);
  748.     startclk = Clock;
  749.  
  750.     /* If output is to the screen, temporarily disable hash marking */
  751.     vsave = ftp->verbose;
  752.     if(vsave >= V_HASH && fp == NULLFILE)
  753.         ftp->verbose = V_NORMAL;
  754.     total = recvfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH) ? ftp->verbose : 0);
  755.     /* Immediately close the data connection; some servers (e.g., TOPS-10)
  756.      * wait for the data connection to close completely before returning
  757.      * the completion message on the control channel
  758.      */
  759.     close_s(ftp->data);
  760.     ftp->data = -1;
  761.  
  762. #ifdef    CPM
  763.     if(fp != NULLFILE && ftp->type == ASCII_TYPE)
  764.         fputc(CTLZ,fp);
  765. #endif
  766.     if(fp != NULLFILE && fp != stdout)
  767.         fclose(fp);
  768.     startclk = Clock - startclk;
  769.     if(startclk != 0)
  770.         rate = total/startclk;
  771.     else
  772.         rate = 0;
  773.     if(total != -1) {
  774.         if(ftp->verbose >= V_HASH)
  775.             tprintf("\n");
  776.         if(ftp->verbose >= V_SHORT)
  777.             tprintf("Get complete: %lu bytes in %lu sec (%lu/sec)\n",
  778.              total,(startclk*MSPTICK)/1000,(rate*1000)/MSPTICK);
  779.     } else {
  780.         tprintf("Error or abort during data transfer\n");
  781.     }
  782.     /* Get the "Sent" message */
  783.     getresp(ftp,200);
  784.  
  785.     ftp->state = COMMAND_STATE;
  786.     ftp->verbose = vsave;
  787.     return 0;
  788. }
  789.  
  790. /* Send a file. Syntax: put <local name> [<remote name>] */
  791. static int doput(argc,argv,p)
  792. int argc;
  793. char *argv[];
  794. void *p;
  795. {
  796.     char *remotename,*localname,*mode;
  797.     register struct ftpcli *ftp;
  798.     int i,resp,control;
  799.     unsigned long total;
  800.     FILE *fp;
  801.     struct sockaddr_in lsocket,lcsocket;
  802.     int32 startclk,rate;
  803.  
  804.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  805.         return -1;
  806.     control = ftp->control;
  807.  
  808.     if(ftp == NULLFTP){
  809.         tprintf(Notsess);
  810.         return 1;
  811.     }
  812.     localname = argv[1];
  813.     if(argc < 3)
  814.         remotename = localname;
  815.     else
  816.         remotename = argv[2];
  817.  
  818.     if(ftp->type == IMAGE_TYPE)
  819.         mode = READ_BINARY;
  820.     else
  821.         mode = READ_TEXT;
  822.  
  823.     /* Send TYPE message, if necessary */
  824.     if(ftp->typesent != ftp->type){
  825.         switch(ftp->type){
  826.         case ASCII_TYPE:
  827.             usprintf(control,"TYPE A\n");
  828.             break;
  829.         case IMAGE_TYPE:
  830.             usprintf(control,"TYPE I\n");
  831.             break;
  832.         case LOGICAL_TYPE:
  833.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  834.             break;
  835.         }
  836.         ftp->typesent = ftp->type;
  837.         resp = getresp(ftp,200);
  838.         if(resp == -1 || resp > 299){
  839.             return 1;
  840.         }
  841.     }
  842.     if((fp = fopen(localname,mode)) == NULLFILE){
  843.         tprintf("Can't read %s: %s\n",localname,sys_errlist[errno]);
  844.         return 1;
  845.     }
  846.     if(ftp->type == ASCII_TYPE && isbinary(fp)){
  847.         tprintf("Warning: File %s appears to be BINARY\n",localname);
  848.     }
  849.     /* Open the data connection */
  850.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  851.     listen(ftp->data,0);
  852.  
  853.     ftp->state = SENDING_STATE;
  854.  
  855.     /* Send the PORT message and wait for ack. Use the IP address
  856.      * on the local end of our control connection.
  857.      */
  858.     i = SOCKSIZE;
  859.     getsockname(ftp->data,(char *)&lsocket,&i);
  860.     i = SOCKSIZE;
  861.     getsockname(ftp->control,(char *)&lcsocket,&i);
  862.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  863.     sendport(control,&lsocket);
  864.     resp = getresp(ftp,200);
  865.     if(resp == -1 || resp > 299){
  866.         /* Error, quit */
  867.         fclose(fp);
  868.         close_s(ftp->data);
  869.         ftp->data = -1;
  870.         ftp->state = COMMAND_STATE;
  871.         return 1;
  872.     }
  873.     /* Generate the command to start the transfer and wait for ack */
  874.     usprintf(control,"STOR %s\n",remotename);
  875.     resp = getresp(ftp,100);
  876.     if(resp == -1 || resp >= 400){
  877.         /* Error, quit */
  878.         fclose(fp);
  879.         close_s(ftp->data);
  880.         ftp->data = -1;
  881.         ftp->state = COMMAND_STATE;
  882.         return 1;
  883.     }
  884.     /* Wait for the data connection to open. Otherwise the first
  885.      * block of data would go out with the SYN, and this may confuse
  886.      * some other TCPs
  887.      */
  888.     accept(ftp->data,NULLCHAR,(int *)NULL);
  889.  
  890.     startclk = Clock;
  891.  
  892.     total = sendfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH) ? ftp->verbose : 0);
  893.     close_s(ftp->data);
  894.     ftp->data = -1;
  895.     fclose(fp);
  896.  
  897.     startclk = Clock - startclk;
  898.     if(startclk != 0)
  899.         rate = total/startclk;
  900.     else
  901.         rate = 0;
  902.     if(total != -1) {
  903.         if(ftp->verbose >= V_HASH)
  904.             tprintf("\n");
  905.         if(ftp->verbose >= V_SHORT)
  906.         tprintf("Put complete: %lu bytes in %lu sec (%lu/sec)\n",
  907.          total,(startclk*MSPTICK)/1000,(rate*1000)/MSPTICK);
  908.     } else {
  909.         tprintf("Error or abort during data transfer\n");
  910.     }
  911.     getresp(ftp,200);
  912.     ftp->state = COMMAND_STATE;
  913.     return 1;
  914. }
  915.  
  916. /* Abort a GET or PUT operation in progress. Note: this will leave
  917.  * the partial file on the local or remote system
  918.  */
  919. int doabort(argc,argv,p)
  920. int argc;
  921. char *argv[];
  922. void *p;
  923. {
  924.     register struct session *sp;
  925.     register struct ftpcli *ftp;
  926.  
  927.     sp = (struct session *)p;
  928.     if(sp == NULLSESSION)
  929.         return -1;
  930.  
  931.     /* Default is the current session, but it can be overridden with
  932.      * an argument.
  933.      */
  934.     if(argc > 1)
  935.         sp = sessptr(argv[1]);
  936.  
  937.     if(sp == NULLSESSION || sp->type != FTP){
  938.         tprintf("Not an active FTP session\n");
  939.         return 1;
  940.     }
  941.     ftp = sp->cb.ftp;
  942.  
  943.     switch(ftp->state){
  944.     case COMMAND_STATE:
  945.         tprintf("No active transfer\n");
  946.         return 1;
  947.     case SENDING_STATE:
  948.         /* Send a premature EOF.
  949.          * Unfortunately we can't just reset the connection
  950.          * since the remote side might end up waiting forever
  951.          * for us to send something.
  952.          */
  953.         shutdown(ftp->data,1);
  954.         break;
  955.     case RECEIVING_STATE:
  956.         /* Just blow away the receive socket */
  957.         shutdown(ftp->data,2);
  958.         break;
  959.     }
  960.     return 0;
  961. }
  962.  
  963. /* send PORT message */
  964. static void sendport(s,socket)
  965. int s;
  966. struct sockaddr_in *socket;
  967. {
  968.     /* Send PORT a,a,a,a,p,p message */
  969.     usprintf(s,"PORT %u,%u,%u,%u,%u,%u\n",
  970.         hibyte(hiword(socket->sin_addr.s_addr)),
  971.         lobyte(hiword(socket->sin_addr.s_addr)),
  972.         hibyte(loword(socket->sin_addr.s_addr)),
  973.         lobyte(loword(socket->sin_addr.s_addr)),
  974.         hibyte(socket->sin_port),
  975.         lobyte(socket->sin_port));
  976. }
  977.  
  978. /* Wait for, read and display response from FTP server. Return the result code.
  979.  */
  980. static int getresp(ftp,mincode)
  981. struct ftpcli *ftp;
  982. int mincode;    /* Keep reading until at least this code comes back */
  983. {
  984.     register char *line;
  985.     int rval;
  986.  
  987.     line = mallocw(LINELEN);
  988.     for(;;){
  989.         /* Get line */
  990.         if(recvline(ftp->control,line,LINELEN) == -1){
  991.             rval = -1;
  992.             break;
  993.         }
  994.         rip(line);        /* Remove cr/lf */
  995.         rval = atoi(line);
  996.         if(rval >= 400 || ftp->verbose >= V_NORMAL)
  997.             tprintf("%s\n",line);    /* Display to user */
  998.  
  999.         /* Messages with dashes are continued */
  1000.         if(line[3] != '-' && (rval = atoi(line)) >= mincode)
  1001.             break;
  1002.     }
  1003.     free(line);
  1004.     return rval;
  1005. }
  1006.  
  1007. /* Issue a prompt and read a line from the user */
  1008. static int getline(sp,prompt,buf,n)
  1009. struct session *sp;
  1010. char *prompt;
  1011. char *buf;
  1012. int n;
  1013. {
  1014.     /* If there's something already there, don't issue prompt */
  1015.     if(socklen(sp->input,0) == 0)
  1016.         tprintf(prompt);
  1017.  
  1018.     usflush(sp->output);
  1019.     return recvline(sp->input,buf,n);
  1020. }
  1021.  
  1022. /* Attempt to log in the user whose name is in ftp->username and password
  1023.  * in pass
  1024.  */
  1025. static char *ftpcli_login(ftp,host)
  1026. struct ftpcli *ftp;
  1027. char *host;
  1028. {
  1029.     char buf[80],*cp,*cp1;
  1030.     FILE *fp;
  1031.  
  1032.     extern char *Hostfile;    /* List of user names and permissions */
  1033.  
  1034.     if((fp = fopen(Hostfile,"r")) == NULLFILE){
  1035.         return NULLCHAR;
  1036.     }
  1037.     while(fgets(buf,sizeof(buf),fp),!feof(fp)){
  1038.         buf[strlen(buf)-1] = '\0';    /* Nuke the newline */
  1039.         if(buf[0] == '#')
  1040.             continue;    /* Comment */
  1041.         if((cp = strchr(buf,' ')) == NULLCHAR)
  1042.             /* Bogus entry */
  1043.             continue;
  1044.         *cp++ = '\0';        /* Now points to user name */
  1045.         if(strcmp(host,buf) == 0)
  1046.             break;        /* Found host name */
  1047.     }
  1048.     if(feof(fp)){
  1049.         /* User name not found in file */
  1050.         fclose(fp);
  1051.         return NULLCHAR;
  1052.     }
  1053.     fclose(fp);
  1054.     /* Look for space after user field in file */
  1055.     if((cp1 = strchr(cp,' ')) == NULLCHAR)
  1056.         /* if not there then we'll prompt */
  1057.         ftp->password = NULLCHAR;
  1058.     else
  1059.         *cp1++ = '\0';        /* Now points to password */
  1060.         if(strcmp(cp,"*") == 0)
  1061.             cp1 = "anonymous";
  1062.         ftp->password = strdup(cp1);
  1063.     return strdup(cp);
  1064. }
  1065.  
  1066. static int doftphelp(argc,argv,p)
  1067. int argc;
  1068. char *argv[];
  1069. void *p;
  1070. {
  1071.     register struct cmds *cmdp;
  1072.     char buf[77];
  1073.     int i;
  1074.  
  1075.     tprintf("Commands available are:\n");
  1076.     memset(buf,' ',sizeof(buf));
  1077.     buf[75] = '\n';
  1078.     buf[76] = '\0';
  1079.     cmdp = Ftpcmds;    /* Skip initial NULL */
  1080.     cmdp++;        /* Skip initial NULL */
  1081.     for(i = 0;cmdp->name != NULLCHAR;cmdp++,i = (i+1)%5) {
  1082.         strncpy(&buf[i*15],cmdp->name,strlen(cmdp->name));
  1083.         if(i == 4) {
  1084.             tprintf(buf);
  1085.             memset(buf,' ',sizeof(buf));
  1086.             buf[75] = '\n';
  1087.             buf[76] = '\0';
  1088.         }
  1089.     }
  1090.     if(i != 0)
  1091.         tprintf(buf);
  1092.     return 0;
  1093. }
  1094.  
  1095.